1 /* 2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021 3 License: [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License]. 4 Authors: Marcelo S. N. Mancini 5 6 Copyright Marcelo S. N. Mancini 2018 - 2021. 7 Distributed under the CC BY-4.0 License. 8 (See accompanying file LICENSE.txt or copy at 9 https://creativecommons.org/licenses/by/4.0/ 10 */ 11 module hip.util.file; 12 import hip.util.conv:to; 13 import hip.util.array:join, array; 14 import hip.util.system; 15 import hip.util.path; 16 import hip.util.string; 17 18 19 string getFileContent(string path, bool noCarriageReturn = true) 20 { 21 import core.stdc.stdio; 22 path = sanitizePath(path); 23 FILE* file = fopen((path~"\0").ptr, "r"); 24 if(!file) 25 return ""; 26 char[] buffer; 27 28 fseek(file, 0, SEEK_END); 29 auto size = ftell(file); 30 fseek(file, 0, SEEK_SET); 31 32 buffer.length = cast(typeof(buffer.length))size; 33 size_t readSize = fread(buffer.ptr, cast(size_t)size, 1, file); 34 if(readSize != buffer.length) 35 buffer.length = readSize; 36 fclose(file); 37 38 string content = cast(string)buffer; 39 return (noCarriageReturn) ? content.replaceAll('\r') : content; 40 } 41 42 43 44 string stripLineBreaks(string content) 45 { 46 content = content.replaceAll('\r'); 47 content = content.replaceAll('\n'); 48 return content; 49 } 50 51 // string getFileContentFromBasePath(string path, string basePath, bool noCarriageReturn = true) 52 // { 53 // string finalPath = relativePath(sanitizePath(path), sanitizePath(basePath)); 54 // return getFileContent(finalPath, noCarriageReturn); 55 // } 56 57 58 59 version(HipDStdFile) 60 { 61 import std.stdio:File; 62 63 version(CustomRuntimeTest) version = CustomRuntime; 64 version(WebAssembly) version = CustomRuntime; 65 version(PSVita) version = CustomRuntime; 66 67 version(CustomRuntimeTest) {void fileTruncate(File file, ptrdiff_t offset){}} 68 else 69 { 70 import std.file; 71 void fileTruncate(File file, ptrdiff_t offset) 72 { 73 version (Windows) 74 { 75 import hip.util.windows; 76 file.seek(offset); 77 if(!SetEndOfFile(file.windowsHandle())) 78 throw new FileException(file.name, "SetEndOfFile error"); 79 } 80 81 version (Posix) 82 { 83 import core.sys.posix.unistd: ftruncate; 84 int res = ftruncate(file.fileno(), offset); 85 if(res != 0) 86 throw new FileException(file.name, "ftruncate error with code "~to!string(res)); 87 } 88 } 89 } 90 } 91 92 version(HipDStdFile) class FileProgression 93 { 94 protected ulong progress; 95 protected uint stepSize; 96 protected ulong fileSize; 97 protected void delegate(ref ubyte[] data) onFinish; 98 protected void delegate(float progress) onUpdate; 99 ubyte[] fileData; 100 ubyte[] buffer; 101 File target; 102 103 /** 104 * If bytes == 0, it will use readsteps. 105 * 106 * Readsteps default is by progressing from 0 to 100, which makes great for percentage. Also notice that with 100 readsteps there's almost 107 * no loss compared to reading the file in one go, so it is the recommended value. 108 * 109 * The greater the readsteps, the more time it will take to read the file and the more precision the percentage will have. Usually 100 should be enough. 110 * 111 * If the readsteps are greater than the filesize, it will clamp to fileSize as readsteps. 112 * That means it will update at every byte 113 */ 114 this(string filePath, uint readSteps = 100, uint bytes = 0) 115 { 116 assert(readSteps != 0 || bytes != 0, "Can't have readSteps and bytes both == 0"); 117 progress = 0; 118 target = File(filePath, "r"); 119 ulong fSize = fileSize = target.size; 120 assert(cast(uint)fSize < uint.max, "Filesize is greater than uint.max, contact the FileProgression mantainer"); 121 fileData.reserve(cast(uint)fSize); 122 123 if(readSteps > fSize) 124 readSteps = cast(uint)fSize; 125 if(bytes != 0) 126 readSteps = cast(uint)fSize/bytes; 127 128 real sz =cast(real)fSize/readSteps; 129 if(sz != fSize/readSteps) //Odd 130 { 131 size_t remaining = cast(size_t)((sz-(fSize/readSteps))*readSteps); 132 buffer = new ubyte[remaining]; 133 target.rawRead(buffer); 134 fileData~= buffer[]; 135 progress+= remaining; 136 stepSize = cast(uint)(fSize-remaining)/readSteps; 137 } 138 else 139 stepSize = cast(uint)fSize/readSteps; 140 buffer.length = stepSize; 141 } 142 143 void setOnFinish(void delegate(ref ubyte[] data) onFinish){this.onFinish = onFinish;} 144 void setOnUpdate(void delegate(float progress) onUpdate){this.onUpdate = onUpdate;} 145 146 bool update() 147 { 148 target.rawRead(buffer); 149 fileData~= buffer[]; 150 progress+=stepSize; 151 if(onUpdate) 152 onUpdate(getProgress()); 153 bool finished = progress >= fileSize; 154 if(finished) 155 { 156 target.close(); 157 if(onFinish) 158 onFinish(this.fileData); 159 } 160 161 return !finished; 162 } 163 164 165 float getProgress(){return progress/cast(float)fileSize;} 166 @property ulong readSize(){return fileData.length;} 167 @property ulong size(){return fileSize;} 168 169 170 override string toString(){return cast(string)fileData;} 171 }